#include "multicastconnector.h"
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <sstream>
using std::cout;
using std::endl;
using std::ostringstream;

#include <Poco/AutoPtr.h>
using Poco::AutoPtr;

#include <Poco/Exception.h>
using Poco::Exception;

#include "notifications/commandnotification.h"

#include "command/commandqueue.h"

#include "command/node/joinringcommand.h"

#include <Poco/Thread.h>
using Poco::Thread;

#include <exception>
using std::exception;

#include "command/unknowncommand.h"
#include "command/node/sendinfocommand.h"
#include "command/network/runcommand.h"
#include "command/commandfactory.h"
#include <Poco/DOM/Element.h>
#include <Poco/DOM/DOMWriter.h>
using Poco::XML::Element;
using Poco::XML::DOMWriter;

#include <Poco/Net/Socket.h>

using namespace Poco::Net;
using namespace Commands;

MulticastConnector::MulticastConnector()
{
	cout << "MulticastConnector :: Constructing a Multicastconnector" << endl;
}

MulticastConnector::MulticastConnector(SocketAddress groupport, CommandQueue *commands) :
	_mcast(),
	_group(groupport),
	cmds( commands )
{
	cout << "MulticastConnector :: Constructing a Multicastconnector" << endl;
	_mcast.bind(SocketAddress(IPAddress(),_group.port()),true);
	_mcast.joinGroup(_group.host());
	_mcast.setLoopback(false);
}

MulticastConnector::MulticastConnector(IPAddress group, UInt16 port, CommandQueue *commands) :
	_mcast(),
	_group(group, port),
	cmds(commands)
	{
	cout << "MulticastConnector :: Constructing a Multicastconnector" << endl;
	_mcast.bind(SocketAddress(IPAddress(),_group.port()),true);
	_mcast.joinGroup(_group.host());
	_mcast.setLoopback(true);
}

MulticastConnector::~MulticastConnector()
{
	cout << "MulticastConnector :: Destroying a Multicastconnector" << endl;
	stop();
	cout << "MulticastConnector :: MC Destroyed" << endl;
}

void MulticastConnector::onTimer(Timer &timer)
{
	//cout << "MulticastConnector :: Timer expired" << endl;
	//cout << "MulticastConnector :: Sending Ring Info" << endl;
	sendRingInfo();
}

const string MulticastConnector::requestRingInfo(){
	cout << "MulticastConnector :: Starting to listen for multicast on " << _group.host().toString() << ":" << _group.port() << endl;
	cout << "MutlicastConnector :: First, I'll listen for 10 secs to see if a group exists" << endl;

	cout << "MulticastConnector :: Listening..." << endl;
	char buffer[1500] = {0};
	long seconds = 5;
	long milis = 0;
	_mcast.setReceiveTimeout(Poco::Timespan(seconds,milis));
	SocketAddress sender;
	try{
		int len = _mcast.receiveFrom(buffer, sizeof(buffer), sender);
		string msg(buffer);
		cout << "MulticastConnector :: Received " << len << "B :: " << buffer << " from: "<< sender.toString() << endl;
		return msg;
	} catch (exception e){
		cout << "MulticastConnector :: Did not recieve anything, broadcasting my own ring info" << endl;
		throw;
		//sendRingInfo();

	}
}

void MulticastConnector::run()
/**
 * Listens for multicasttraffic on a given socket. If it recieves a command it tries to execute it.
 */
{
	cout << "MulticastConnector :: Going to listen to multicasttraffic" << endl;
	while(true){
		//Thread::sleep(2000);
		//cout << "MulticastConnector :: Listening..." << endl;
		try{
			SocketAddress sender;
			char buffer[1500];
			int len = _mcast.receiveFrom(buffer, sizeof(buffer), sender);
			string msg(buffer);
			//cout << "MulticastConnector :: Received " << len << "B :: \"" << msg << "\"" "from: "<< sender.toString() << endl;
			//FIXME let commandfactory construct the object (or at
			//least let it try), if it fails, wrap the repr in an
			//UnknownCommand. Everything MC related should be able
			//to happen here with commands though...
			if (CommandFactory::isCommand(msg)){
				Command* cmd = new UnknownCommand(msg, sender, _mcast.address());
				//cout << "MulticastConnector :: Pushing commandinfo on the queue" << endl;
				cmds->push(cmd);
				//cout << "MulticastConnector :: Recieved command is on queue, Node has to handle it now.." << endl;
			}
		}catch(exception e){
			//cout << "MulticastConnector :: Not recieving any messages" << endl;
			//sendRingInfo();
		}
	}
	cout << "MulticastConnector :: Thread Stopping" << endl;
}

void MulticastConnector::sendRingInfo(){
	//FIXME Is this really necessary? What about each node (or
	//multicastconnector) just respond to RequestInfo commands made on the
	//'public' group address?
	/*while (true)
	{*/
		try{
			string name = "cecilia";
			Document* doc = new Document();
			Element* ring = doc->createElement("ring");
			ring->setAttribute("name",name);
			doc->appendChild(ring);
		//	cout << "MulticastConnector :: Sending..." << endl;
			ostringstream out;
			DOMWriter w;
		//	cout << "MulticastConnector :: Wrting Node" << endl;
			out.flush();
			w.writeNode(out, doc);
		//	cout << "MulticastConnector :: Node written" << endl;
			/*char *buf[1500]
			char buffer[1500];
			strcpy( buffer, out.str().c_str() );*/
			string buffer = out.str();
		//	cout << "MulticastConnector :: Sending data (" << buffer.size() << "B): '''" << buffer << "'''" << endl;
			_mcast.sendTo(buffer.c_str(), buffer.size() , _group);
		//	cout << "MulticastConnector :: Sent " << bytes << " to the mc group" << endl;
		} catch (exception e){
			cout << "MulticastConnector :: Something went wrong with sending info" << endl;
		}
	//}
}

void MulticastConnector::sendCommand(string const& repr)
/**
 * Sends a command on the ring
 */
{	
	cout << "MulticastConnector :: Sending command: \"" << repr << "\"" << endl;
	_mcast.sendTo(repr.c_str(),repr.size(),_group);
	cout << "MulticastConnector :: Command sent" << endl;
}

void MulticastConnector::stop()
{
	_mcast.leaveGroup(_group.host());
	_mcast.close();
}

string MulticastConnector::getAddress()
{
	return _mcast.address().host().toString();
}
